package com.quectel.core.module.propertybill.service.impl;


import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.quectel.constant.core.household.HouseholdConstants;
import com.quectel.constant.core.propertybill.PropertyBillConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.util.aspect.lock.annotation.Lock;
import com.quectel.core.constants.RedisCacheConstants;
import com.quectel.core.constants.RedisLockConstants;
import com.quectel.core.module.household.dto.HouseholdRoomDto;
import com.quectel.core.module.household.service.HouseholdRoomService;
import com.quectel.core.module.household.service.HouseholdService;
import com.quectel.core.module.property.dto.PropertyInfoDto;
import com.quectel.core.module.property.service.PropertyInfoService;
import com.quectel.core.module.propertybill.dao.PropertyBillDao;
import com.quectel.core.module.propertybill.dto.*;
import com.quectel.core.module.propertybill.entity.PropertyBillEntity;
import com.quectel.core.module.propertybill.service.*;
import com.quectel.core.module.propertybill.vo.PropertyBillAmountCountVo;
import com.quectel.core.module.propertybill.vo.PropertyBillAmountVo;
import com.quectel.core.module.propertybill.vo.PropertyChargeableListActivityVo;
import com.quectel.core.module.village.service.VillageRoomService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.kit.CacheKit;
import com.quectel.util.kit.Snowflake;
import com.quectel.util.mybatis.MyBatisPlusUtils;
import com.quectel.util.spring.SpringUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author zhangheng
 * @email nolan.zhang@quectel.com
 * @date 2023/03/31 16:28
 */
@DubboService
public class PropertyBillServiceImpl implements PropertyBillService {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyBillService.class);

    /**
     * 入参解释:实体id
     */
    private static final Function<Long, String> PROPERTY_BILL_CACHE_BY_ID_FUNC = id -> String.format(RedisCacheConstants.PROJECT_NAME + "PROPERTY_BILL_CACHE_BY_ID:%d", id);

    /**
     * 清除缓存方法
     */
    private static final Consumer<PropertyBillDto> CLEAR_CACHE_FUNC = dto -> {
        CacheKit.invalidRedisCache(PROPERTY_BILL_CACHE_BY_ID_FUNC.apply(dto.getId()));
    };
    @Autowired
    private PropertyInfoService propertyInfoService;
    @Autowired
    private PropertyBillDao propertyBillDao;
    @Autowired
    private VillageRoomService villageRoomService;
    @Autowired
    private PropertyBillActivityService propertyBillActivityService;
    @Autowired
    private HouseholdService householdService;
    @Autowired
    private HouseholdRoomService householdRoomService;
    @Autowired
    private PropertyChargeableListService propertyChargeableListService;
    @Autowired
    private PropertyBillChangeableSnapshotService propertyBillChangeableSnapshotService;
    @Autowired
    private PropertyChargeableListDueDateService propertyChargeableListDueDateService;


    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PROPERTY_BILL_LOCK + "#{#dto.id}"
            }
    )
    @Transactional(rollbackFor = RuntimeException.class)
    public void confirmActivity(Long id) {
        PropertyBillService propertyBillService = SpringUtils.getBean(PropertyBillService.class);

        //修改赠品状态为已确认
        List<PropertyBillActivityDto> propertyBillActivityDtos = propertyBillActivityService.selectByBillId(id);
        if (CollectionUtils.isNotEmpty(propertyBillActivityDtos)) {
            for (PropertyBillActivityDto propertyBillActivityDto : propertyBillActivityDtos) {
                propertyBillActivityDto.setConfirmStatus(SystemConstants.NORMAL);
                propertyBillActivityDto.setConfirmTime(new Date());
                propertyBillActivityService.updateById(propertyBillActivityDto);
            }
        }
        PropertyBillDto propertyBillDto = propertyBillService.selectById(id);
        propertyBillDto.setGiftFlag(SystemConstants.YES);
        propertyBillService.updateById(propertyBillDto);
    }

    @Override
    public List<Long> queryFloorIds(Map<String, Object> params) {
        return propertyBillDao.queryFloorIds(params);
    }

    @Override
    public List<Long> queryBuildingIds(Map<String, Object> params) {
        return propertyBillDao.queryBuildingIds(params);
    }


    @Override
    public int countRoom(Map<String, Object> params) {
        return propertyBillDao.countRoom(params);
    }

    @Override
    public PropertyBillAmountCountVo countAmount(Map<String, Object> params) {
        return propertyBillDao.countAmount(params);
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PROPERTY_BILL_LOCK + "#{#dto.id}"
            }
    )
    @Transactional
    public Long saveOrUpdateWithAmountVO(PropertyBillDto dto) {

        if (dto.getId() != null) {
            //空值覆盖问题
            updateAllColumnById(dto);
        } else {
            dto.setId(save(dto));

        }

        //更新镜像
        //删除镜像信息
        propertyBillChangeableSnapshotService.deleteByBillId(dto.getId());
        PropertyBillChangeableSnapshotDto snapshotDto = new PropertyBillChangeableSnapshotDto();
        snapshotDto.setBillId(dto.getId());
        if (dto.getPropertyBillAmount() != null) {
            snapshotDto.setPropertyBillAmountJson(JacksonUtils.toJsonString(dto.getPropertyBillAmount()));
        }
        //重新保存
        propertyBillChangeableSnapshotService.save(snapshotDto);

        return dto.getId();
    }


    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PROPERTY_CHARGEABLE_LIST_DUE_DATE_LOCK + "#{#dto.roomId}" + ":" + "#{#dto.chargeableListId}"
            }
    )
    public void billPaySuccessUpdateDueDate(PropertyBillDto dto) {

        PropertyBillDto dbBill = selectById(dto.getId());
        if (dbBill == null || PropertyBillConstants.PayStatus.PAY_SUCCESS.getStatusValue().equals(dbBill.getPayStatus())) {
            return;
        }

        // 账单更新
        PropertyBillDto newUpdateObj = new PropertyBillDto();
        newUpdateObj.setId(dto.getId());
        newUpdateObj.setOrderId(dto.getOrderId());
        newUpdateObj.setOrderNo(dto.getOrderNo());
        newUpdateObj.setPayStatus(PropertyBillConstants.PayStatus.PAY_SUCCESS.getStatusValue());
        newUpdateObj.setPayTime(dto.getPayTime());
        newUpdateObj.setUpdateTime(dto.getUpdateTime());
        SpringUtils.getBean(PropertyBillService.class).updateById(newUpdateObj);

        PropertyBillAmountVo propertyBillAmount = dbBill.getPropertyBillAmount();

        //保存赠品记录
        List<PropertyChargeableListActivityVo> giftList = propertyBillAmount.getGiftList();
        //保存赠品记录
        if (CollectionUtils.isNotEmpty(giftList)) {
            for (PropertyChargeableListActivityVo propertyChargeableListActivityVo : giftList) {
                PropertyBillActivityDto propertyBillActivityDto = new PropertyBillActivityDto();
                propertyBillActivityDto.setTenantId(dbBill.getTenantId());
                propertyBillActivityDto.setVillageId(dbBill.getVillageId());
                propertyBillActivityDto.setRoomId(dbBill.getRoomId());
                propertyBillActivityDto.setBillId(dbBill.getId());
                propertyBillActivityDto.setActivityName(propertyChargeableListActivityVo.getName());
                propertyBillActivityDto.setConfirmStatus(SystemConstants.ABNORMAL);
                propertyBillActivityService.save(propertyBillActivityDto);
            }
        }

        // 更新结束日期
        PropertyChargeableListDueDateDto dueDateDto = propertyChargeableListDueDateService.selectByRoomInfoAndListId(dto.getTenantId(), dto.getVillageId(), dto.getRoomId(), dto.getChargeableListId());
        if (dueDateDto == null || dueDateDto.getDueDate() == null) {
            return;
        }

        //如果dueDate为空或者新的dueDate在老的日期之后,才可以更新
        dueDateDto.setDueDate(
                DateUtil.offsetMonth(
                        dueDateDto.getDueDate(),
                        propertyBillAmount.getBillCycleDateInfoVo().getTotalMonths()
                )
        );
        propertyChargeableListDueDateService.updateById(dueDateDto);


    }


    @Override
    public PropertyBillDto selectCacheById(Long id) {
        return CacheKit.cacheToRedis(
                () -> selectById(id),
                PROPERTY_BILL_CACHE_BY_ID_FUNC.apply(id),
                SystemConstants.NOT_NULL_CACHE_EXPIRE_SECONDS
        );
    }

    @Override
    public PropertyBillDto selectById(Long id) {
        PropertyBillEntity entity = propertyBillDao.selectById(id);
        PropertyBillDto propertyBillDto = paddingField(CopyUtils.copyObj(entity, PropertyBillDto.class));

        if (propertyBillDto != null) {
            //查询镜像信息
            PropertyBillChangeableSnapshotDto propertyBillChangeableSnapshotDto = propertyBillChangeableSnapshotService.selectByBillId(id);
            if (propertyBillChangeableSnapshotDto != null) {
                propertyBillDto.setPropertyBillAmount(propertyBillChangeableSnapshotDto.getPropertyBillAmountVo());
                //缴费人
                propertyBillDto.setHousehold(householdService.selectCacheById(propertyBillDto.getHouseholdId()));
            }
        }

        return propertyBillDto;
    }

    @Override
    public List<PropertyBillDto> queryList(Map<String, Object> params) {
        List<PropertyBillEntity> list = propertyBillDao.queryList(params);
        List<PropertyBillDto> result = CopyUtils.copyList(list, PropertyBillDto.class);
        result.forEach(this::paddingField);
        return result;
    }

    /**
     * 此方法慎用 禁止填充List一类 比如一个人有多个地址这里不允许填充
     * 填充的原则是: 1:被填充对象不经常变换 2:根据id可以走缓存 3数据足够简单 比如设备类型 所属园区等
     *
     * @param dto
     * @return
     */
    private PropertyBillDto paddingField(PropertyBillDto dto) {
        if (dto != null) {
            //房间
            dto.setVillageRoom(villageRoomService.selectCacheById(dto.getRoomId()));
            //收费标准
            PropertyChargeableListDto propertyChargeableListDto = propertyChargeableListService.selectCacheById(dto.getChargeableListId());
            if (propertyChargeableListDto != null) {
                dto.setChargeableListName(propertyChargeableListDto.getName());
            }

            //物业公司
            PropertyInfoDto propertyInfoDto = propertyInfoService.selectCacheByVillageId(dto.getVillageId());
            dto.setPropertyInfoName(propertyInfoDto != null ? propertyInfoDto.getName() : "");

            List<HouseholdRoomDto> householdRoomDtos = householdRoomService.selectByRoomIdAndIdentityTypes(dto.getRoomId(), Collections.singletonList(HouseholdConstants.IDENTITY_CATEGORY_OWNER));
            if (CollectionUtils.isNotEmpty(householdRoomDtos)) {
                dto.setOwnerMobile(householdRoomDtos.get(0).getHouseholdDto().getMobile());
                dto.setOwnerName(householdRoomDtos.get(0).getHouseholdDto().getName());
            }

            if(dto.getPayTime() == null){
                if(dto.getOverdueDate() != null && dto.getOverdueDate().before(new Date())){
                    dto.setOverdueStatus(PropertyBillConstants.OverdueStatus.OVERDUE.getStatus());
                    //逾期日期
                    long between = DateUtil.between(dto.getOverdueDate(), new Date(), DateUnit.DAY);
                    dto.setOverdueDays(between);
                }else{
                    dto.setOverdueStatus(PropertyBillConstants.OverdueStatus.NOT_OVERDUE.getStatus());
                    dto.setOverdueDays(0L);
                }

            }else{
                if(dto.getOverdueDate().before(dto.getPayTime())){
                    dto.setOverdueStatus(PropertyBillConstants.OverdueStatus.OVERDUE.getStatus());
                    //逾期日期
                    long between = DateUtil.between(dto.getOverdueDate(), dto.getPayTime(), DateUnit.DAY);
                    dto.setOverdueDays(between);
                }else{
                    dto.setOverdueStatus(PropertyBillConstants.OverdueStatus.NOT_OVERDUE.getStatus());
                    dto.setOverdueDays(0L);
                }
            }

        }
        return dto;
    }

    @Override
    public int queryTotal(Map<String, Object> params) {
        return propertyBillDao.queryTotal(params);
    }

    @Override
    public Long save(PropertyBillDto dto) {
        PropertyBillEntity entity = CopyUtils.copyObj(dto, PropertyBillEntity.class);
        entity.setId(Snowflake.nextId());
        propertyBillDao.insert(entity);
        return entity.getId();
    }

    @Override
    @Lock(
            lockExpresses = {
                    RedisLockConstants.PROPERTY_BILL_LOCK + "#{#dto.id}"
            }
    )
    public void updateById(PropertyBillDto dto) {
        PropertyBillEntity entity = CopyUtils.copyObj(dto, PropertyBillEntity.class);

        propertyBillDao.updateById(entity);

        CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    public void updateAllColumnById(PropertyBillDto dto) {
        PropertyBillEntity entity = CopyUtils.copyObj(dto, PropertyBillEntity.class);

        MyBatisPlusUtils.updateAllColumnById(entity, propertyBillDao);

        CLEAR_CACHE_FUNC.accept(dto);
    }

    @Override
    public void deleteById(Long id) {

        CLEAR_CACHE_FUNC.accept(selectById(id));

        propertyBillDao.deleteById(id);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            deleteById(id);
        }
    }
}
